
interface IAny {
  [key: string]: any
}

export default function customWatch<T extends IAny>(state: T) {

  let subscribe = <{
    [k in keyof T & string]: Array<(newVal?: T[k], oldVal?: T[k]) => void>
  }>{}

  for(let key in state) {
    if(state.hasOwnProperty(key)) {
      defineReactive(state, key, state[key])
    }
  }

  function defineReactive<K extends keyof T & string>(obj: T, key: K, value: T[K]) {
    Object.defineProperty(obj, key, {
      get() {
        return value
      },
      set(newVal) {
        if(newVal !== value) {
          if(subscribe[key]) {
            subscribe[key].forEach(cb => cb(newVal, value))
          }
          value = newVal
        }
      }
    })
  }

  function on<K extends keyof T & string>(eventName: `${K}Change`, callback: (newVal?: T[K], oldVal?: T[K]) => void) {
    const key = eventName.replace('Change', '') as keyof T & string
    subscribe[key] = subscribe[key] || []
    subscribe[key].push(callback)
  }

  function off<K extends keyof T & string>(eventName?: `${K}Change`, callback?: (newVal?: T[K], oldVal?: T[K]) => void) {
    const key = eventName?.replace('Change', '') as keyof T & string
    const args = Array.from(arguments)
    if(args.length === 0) {
      subscribe = {} as {
        [k in keyof T & string]: Array<((newVal?: T[k], oldVal?: T[k]) => void)>
      }
    } else if(args.length === 1) {
      if(!subscribe[key]) return
      subscribe[key] = []
    } else {
      if(!subscribe[key]) return
      for(let i = 0; i < subscribe[key].length; i++) {
        if(subscribe[key][i] === callback) {
          subscribe[key].splice(i, 1)
          break
        }
      }
    }
  }

  return { state, on, off }
}


// // 使用测试
// const oberver = customWatch({
//   name: 'WFT',
//   age: 18
// })

// oberver.on('nameChange', (newVal, oldVal) => {
//   console.log(newVal, '新值-->')
//   console.log(oldVal, '旧值-->')
// })

// // oberver.off('nameChange')

// oberver.state.name = '哈哈哈哈'